/***************************************************************************
 *  Title: MySimpleShell 
 * -------------------------------------------------------------------------
 *    Purpose: A simple shell implementation 
 *    Author: Stefan Birrer
 *    Version: $Revision: 1.1 $
 *    Last Modification: $Date: 2005/10/13 05:24:59 $
 *    File: $RCSfile: tsh.c,v $
 *    Copyright: (C) 2002 by Stefan Birrer
 ***************************************************************************/
/***************************************************************************
 *  ChangeLog:
 * -------------------------------------------------------------------------
 *    $Log: tsh.c,v $
 *    Revision 1.1  2005/10/13 05:24:59  sbirrer
 *    - added the skeleton files
 *
 *    Revision 1.4  2002/10/24 21:32:47  sempi
 *    final release
 *
 *    Revision 1.3  2002/10/23 21:54:27  sempi
 *    beta release
 *
 *    Revision 1.2  2002/10/15 20:37:26  sempi
 *    Comments updated
 *
 *    Revision 1.1  2002/10/15 20:20:56  sempi
 *    Milestone 1
 *
 ***************************************************************************/

#define __MYSS_IMPL__

/************System include***********************************************/

#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>

/************Private include**********************************************/

#include "tsh.h"
#include "io.h"
#include "interpreter.h"
#include "runtime.h"
#include "config.h"

/************Defines and Typedefs*****************************************/
/*  #defines and typedefs should have their names in all caps.
 *  Global variables begin with g. Global constants with k. Local
 *  variables should be in all lower case. When initializing
 *  structures and arrays, line everything up in neat columns.
 */

typedef struct bgjob_l {
  int jobNumber;
  pid_t pid;
  char* commandLine; // store the name of the job as described by the first argument (e.g. ./hello)
  int state; // 0 = stopped, 1 = running
  struct bgjob_l* next;
} bgjobL; // use a linked list to store the process id

typedef struct fgjob_l {
  pid_t pgid; // if pid = -1, no foreground job
  char* commandLine; // store the name of the job as described by the first argument (e.g. ./hello)
} fgjobL;

#define BUFSIZE 80 // max number of char allowed is 80?

/************Global Variables*********************************************/

/************Function Prototypes******************************************/

/* handles SIGINT and SIGTSTP signals */	
static void sig(int);
void deleteBgJobNode(pid_t, bool);
int appendBgJobNode(pid_t, char*, int);

/************External Declaration*****************************************/

extern bgjobL* bgjobs;
extern fgjobL fgjob;
extern char outputBuffer[BUFFERSIZE];

/**************Implementation***********************************************/

int main (int argc, char *argv[])
{
  /* Initialize command buffer */
  char* cmdLine = malloc(sizeof(char*) * BUFSIZE);

  /* shell initialization */
  // install the signal handler
  if (signal(SIGINT, sig) == SIG_ERR) PrintPError("SIGINT");
  if (signal(SIGTSTP, sig) == SIG_ERR) PrintPError("SIGTSTP");
  if (signal(SIGCHLD, sig) == SIG_ERR) PrintPError("SIGCHLD");

  while (!forceExit) /* repeat forever */
    {
      /* read command line */
      //printf("tsh> ");
      getCommandLine(&cmdLine, BUFSIZE); // read command line into the buffer

      if (feof(stdin) || strcmp(cmdLine, "exit") == 0) {
    	// if eof is set in input stream or the cmdline is exit
    	// feof() test the end of file indicator for the stream
    	// returning non-zero if it is set
	forceExit = TRUE;
	if(feof(stdin)) // if ctrl-d is pressed, start a new line
    	  printf("\n");
	continue;
      }

      if (outputBuffer[0] != '\0') {
    	writeWrapper(outputBuffer, strlen(outputBuffer));
    	*outputBuffer = '\0';
      }

      /* checks the status of background jobs */
      CheckJobs();

      /* interpret and run the command line */
      Interpret(cmdLine);

    }

  /* shell termination */
  free(cmdLine);
  return 0;
} /* end main */

void sendSignal2AllBgJobs(int sig) {
  bgjobL* p = NULL;
  for (p = bgjobs; p != NULL; p = p->next)
    kill(p->pid, sig);
}

static void sig(int signo) {
  pid_t pid;

  switch(signo) {
  case SIGCHLD:
    while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {// pending signal only receive once
      deleteBgJobNode(pid, TRUE);
    }
    break;

    // when the main program received SIGINT, send the signal to fgjob.pgid
  case SIGINT:
    if (fgjob.pgid != -1)
      kill(-fgjob.pgid, SIGINT); // send to fg job
    break;

    // when stopped
    // 1. display a message
    // 2. add the fg job
    // 3. stop the process
  case SIGTSTP:
    if (fgjob.pgid != -1) { // only parent can have a non-negative fgjob.pid, the the following is not triggered in child
      int jobNumber = appendBgJobNode(fgjob.pgid, fgjob.commandLine, 0);
      printf("[%d]   Stopped                 %s\n", jobNumber, fgjob.commandLine);
      fflush(stdout);
      kill(-fgjob.pgid, SIGTSTP); // stop the fg process
      fgjob.pgid = -1; // the fg job moved to bg
    }
  }

  return;
}

